﻿namespace WLFramework.Data
{
    using System;
    using System.Collections.Generic;
    using System.Data;
    using System.Linq;
    using System.Reflection;
    using WLFramework.Common;
    using WLFramework.Reflection;
    using WLFramework.Validation;

    /// <summary>
    /// IDataReader扩展方法
    /// </summary>
    public static class DataReaderExtensions
    {
        private static readonly object[] readIndex = new object[] { 0 };

        /// <summary>
        /// 将IDataReader转换成DataTable类型
        /// </summary>
        /// <param name="reader">IDataReader对象</param>
        /// <returns>查询结果</returns>
        public static DataTable ToDataTable(this IDataReader reader)
        {
            Shield.AssertNotNull(ref reader, "reader");

            var table = new DataTable();
            var fieldCount = reader.FieldCount;
            for (int i = 0; i < fieldCount; i++)
            {
                table.Columns.Add(reader.GetName(i), reader.GetFieldType(i));
            }

            using (reader)
            {
                while (reader.Read())
                {
                    var row = new object[fieldCount];
                    reader.GetValues(row);
                    table.Rows.Add(row);
                }
            }

            return table;
        }

        /// <summary>
        /// 提取IDataReader结果第一列的所有值
        /// </summary>
        /// <typeparam name="TValue">第一列的参数类型 简单基元类型</typeparam>
        /// <param name="reader">IDataReader对象</param>
        /// <returns>查询结果</returns>
        public static IList<TValue> ToFirstColumnList<TValue>(this IDataReader reader)
        {
            return reader.ToListByIndex<TValue>(0);
        }

        /// <summary>
        /// 根据返回结果Column序号提取结果集
        /// </summary>
        /// <typeparam name="TValue">Column类型</typeparam>
        /// <param name="reader">IDataReader对象</param>
        /// <param name="index">Column序号</param>
        /// <returns>查询结果</returns>
        public static IList<TValue> ToListByIndex<TValue>(this IDataReader reader, int index)
        {
            Shield.AssertNotNull(ref reader, "reader");
            Type type = typeof(TValue);
            Type valueType = type.IsGenericType && type.GetGenericTypeDefinition() == Types.Nullable ?
                        type.GetGenericArguments()[0] :
                        type;
            MethodInfo method = null;
            if (valueType != Types.Object)
            {
                if (!DataMapper.GetValueMethods.TryGetValue(valueType, out method))
                {
                }
            }

            List<TValue> list = (List<TValue>)Activator.CreateInstance(typeof(List<TValue>));
            using (reader)
            {
                while (reader.Read())
                {
                    if (method == null)
                    {
                        list.Add((TValue)reader.GetValue(0));
                    }
                    else
                    {
                        list.Add((TValue)
                            (reader.IsDBNull(0) ? null : method.Invoke(reader, new object[] { index })));
                    }
                }
            }

            return list;
        }

        /// <summary>
        /// 根据返回结果Column名称提取结果集
        /// </summary>
        /// <typeparam name="TValue">Column类型</typeparam>
        /// <param name="reader">IDataReader对象</param>
        /// <param name="name">Column名称</param>
        /// <returns>查询结果</returns>
        public static IList<TValue> ToListByColumnName<TValue>(this IDataReader reader, string name)
        {
            Shield.AssertNotNull(ref reader, "reader");

            int index = reader.GetOrdinal(name);
            return reader.ToListByIndex<TValue>(index);
        }

        /// <summary>
        /// 使用函数提取IDataReader中的值
        /// </summary>
        /// <typeparam name="TEntity">实体类型</typeparam>
        /// <param name="reader">IDataReader对象</param>
        /// <param name="creator">实体获取函数</param>
        /// <returns>查询结果</returns>
        public static IList<TEntity> ToList<TEntity>(
            this IDataReader reader, 
            Func<IDataReader, TEntity> creator)
        {
            Shield.AssertNotNull(ref reader, "reader");
            Shield.AssertNotNull(ref creator, "creator");

            var items = new List<TEntity>();
            using (reader)
            {
                while (reader.Read())
                {
                    var item = creator(reader);
                    if (item != null)
                        items.Add(item);
                }
            }
            
            return items;
        }

        /// <summary>
        /// 获取实体类集合
        /// </summary>
        /// <typeparam name="TEntity">实体类型</typeparam>
        /// <param name="reader">IDataReader对象</param>
        /// <returns>查询结果</returns>
        public static IList<TEntity> ToList<TEntity>(this IDataReader reader) where TEntity : new()
        {
            Shield.AssertNotNull(ref reader, "reader");

            List<TEntity> list = new List<TEntity>();
            using (reader)
            {
                var type = typeof(TEntity);
                var columns = GetColumns(reader, type);
                var ctor = CreateCtor(type);

                while (reader.Read())
                {
                    list.Add(CreateObject<TEntity>(reader, ctor, columns));
                }
            }

            return list;
        }

        private static List<TargetSetter> GetColumns(IDataReader reader, Type type)
        {
            var columns = new List<PropertyInfo>(reader.FieldCount);
            var names = reader.FieldCount.GenerateRange(0).Select(i=>reader.GetName(i)).ToList();
            var setters = CreateSetters(type).Where(k => names.IndexOf(k.Name) != -1);

            return setters.ToList();
        }

        private static List<TargetSetter> CreateSetters(Type type)
        {
            return DynamicDelegateFactory.GetSetters(
                        type,
                        (p) =>
                        {
                            var ignore = p.GetAttribute<IgnoreAttribute>(true);
                            return (ignore != null && ignore.IsIgnoreSet);
                        },
                        (f) =>
                        {
                            var ignore = f.GetAttribute<IgnoreAttribute>(true);
                            return ignore != null && ignore.IsIgnoreSet;
                        });
        }

        private static CtorDelegate CreateCtor(Type type)
        {
            return DynamicDelegateFactory.CreateConstructorDelegate(type.GetConstructor(Type.EmptyTypes));
        }

        private static T CreateObject<T>(
            IDataReader reader,
            CtorDelegate ctor,
            List<TargetSetter> setters) where T : new()
        {
            T result = (T)ctor();
            for (int i = 0; i < setters.Count; i++)
            {
                setters[i].Setter(result, reader[setters[i].Name] == DBNull.Value ? null : reader[setters[i].Name]);
            }

            return result;
        }
    }
}
